import FountainCode from './FountainCode';

/**
 * 喷泉码解码器类
 */
export default class FountainDecoder {
    constructor(k, blockSize, originalSize) {
        this.k = k;
        this.blockSize = blockSize;
        this.originalSize = originalSize;
        this.decodedCount = 0;
        this.decodedBlocks = new Array(k).fill(null);
        this.graph = new Array(k)
            .fill()
            .map(() => ({ neighbors: new Set(), data: null }));
        this.solved = new Array(k).fill(false);
        this.unsolvedQueue = [];
        this.robustDistribution =
            new FountainCode().calculateRobustSolitonDistribution(k);
        this.minRequiredPackets = Math.ceil(k * 1.2); // 增加最小所需编码包数量
    }

    /**
     * 处理接收到的编码包
     * @param {Object} encodedBlock 编码包
     * @returns {boolean} 是否有新的原始包被解码
     */
    receivePacket(encodedBlock) {
        const { seed, degree, data } = encodedBlock;
        const random = new FountainCode().getPseudoRandomGenerator(seed);
        const k = this.k;

        // 生成与编码时相同的随机索引
        const neighbors = new Set();
        while (neighbors.size < degree) {
            const index = Math.floor(random() * k);
            neighbors.add(index);
        }

        // 创建新节点
        const node = {
            neighbors,
            data: new Uint8Array(data),
        };

        // 检查是否可以直接解码
        if (neighbors.size === 1) {
            const originalIndex = [...neighbors][0];
            if (!this.solved[originalIndex]) {
                this.solveBlock(originalIndex, node.data);
                return true;
            }
        }

        // 添加到图中
        this.unsolvedQueue.push(node);
        return this.processQueue();
    }

    /**
     * 处理待解码队列
     * @returns {boolean} 是否有新的原始包被解码
     */
    processQueue() {
        let progress = false;

        // 循环处理队列，直到没有进展
        while (true) {
            let processed = 0;

            for (let i = this.unsolvedQueue.length - 1; i >= 0; i--) {
                const node = this.unsolvedQueue[i];

                // 移除已解码的邻居
                for (const index of node.neighbors) {
                    if (this.solved[index]) {
                        // 异或已解码的数据
                        for (let j = 0; j < this.blockSize; j++) {
                            node.data[j] ^= this.decodedBlocks[index][j];
                        }
                        node.neighbors.delete(index);
                    }
                }

                // 如果只剩下一个邻居，那么可以解码
                if (node.neighbors.size === 1) {
                    const originalIndex = [...node.neighbors][0];
                    if (!this.solved[originalIndex]) {
                        this.solveBlock(originalIndex, node.data);
                        progress = true;
                        processed++;
                    }
                    // 从队列中移除
                    this.unsolvedQueue.splice(i, 1);
                }
            }

            // 如果没有处理任何节点，退出循环
            if (processed === 0) {
                break;
            }
        }

        return progress;
    }

    /**
     * 解码一个原始数据包
     * @param {number} index 原始数据包索引
     * @param {Uint8Array} data 解码后的数据
     */
    solveBlock(index, data) {
        this.decodedBlocks[index] = data;
        this.solved[index] = true;
        this.decodedCount++;
    }

    /**
     * 检查是否所有原始数据包都已解码
     * @returns {boolean} 是否完全解码
     */
    isFullyDecoded() {
        return this.decodedCount === this.k;
    }

    /**
     * 获取已解码的原始数据包
     * @returns {Uint8Array[]} 已解码的原始数据包
     */
    getDecodedBlocks() {
        return this.decodedBlocks;
    }

    /**
     * 使用高斯消元法尝试解码剩余的数据包
     * @returns {boolean} 是否成功解码
     */
    tryGaussianElimination() {
        if (this.isFullyDecoded()) {
            return true;
        }

        // 找出未解出的原始数据包索引
        const unsolvedIndices = [];
        for (let i = 0; i < this.k; i++) {
            if (!this.solved[i]) {
                unsolvedIndices.push(i);
            }
        }

        const m = unsolvedIndices.length;
        if (m === 0) {
            return true; // 已经全部解码
        }

        // 确保有足够的方程（增加冗余）
        if (this.unsolvedQueue.length < m * 1.2) {
            // 增加20%的冗余
            return false; // 方程不足，无法解码
        }

        // 创建增广矩阵（使用更多方程提高成功率）
        const equationsNeeded = Math.min(
            this.unsolvedQueue.length,
            Math.ceil(m * 1.5)
        );
        const matrix = Array(equationsNeeded)
            .fill()
            .map(() => Array(m + this.blockSize).fill(0));

        // 填充矩阵
        for (let i = 0; i < equationsNeeded; i++) {
            const node = this.unsolvedQueue[i];

            // 设置系数部分
            for (const neighbor of node.neighbors) {
                const col = unsolvedIndices.indexOf(neighbor);
                if (col !== -1) {
                    matrix[i][col] = 1;
                }
            }

            // 设置数据部分
            for (let j = 0; j < this.blockSize; j++) {
                matrix[i][m + j] = node.data[j];
            }
        }

        // 使用改进的高斯-约旦消元法
        const solved = this.gaussianJordanElimination(
            matrix,
            equationsNeeded,
            m,
            this.blockSize
        );

        if (!solved) {
            return false; // 无法解码
        }

        // 提取解码后的原始数据包
        for (let i = 0; i < m; i++) {
            const originalIndex = unsolvedIndices[i];
            const block = new Uint8Array(this.blockSize);

            for (let j = 0; j < this.blockSize; j++) {
                block[j] = matrix[i][m + j];
            }

            this.solveBlock(originalIndex, block);
        }

        return true;
    }

    /**
   * 高斯-约旦消元法（简化版）
   */
  gaussianJordanElimination(matrix, rows, cols, dataSize) {
    let rowCount = 0;
    
    for (let col = 0; col < cols; col++) {
      // 寻找主元
      let pivotRow = -1;
      for (let row = rowCount; row < rows; row++) {
        if (matrix[row][col] === 1) {
          pivotRow = row;
          break;
        }
      }
      
      if (pivotRow === -1) {
        continue; // 列全为0，跳过
      }
      
      // 交换行
      if (pivotRow !== rowCount) {
        [matrix[rowCount], matrix[pivotRow]] = [matrix[pivotRow], matrix[rowCount]];
      }
      
      // 消元（约旦法：上下都消）
      for (let row = 0; row < rows; row++) {
        if (row !== rowCount && matrix[row][col] === 1) {
          for (let i = col; i < cols + dataSize; i++) {
            matrix[row][i] ^= matrix[rowCount][i];
          }
        }
      }
      
      rowCount++;
      
      if (rowCount >= cols) {
        break; // 已找到足够的主元
      }
    }
    
    // 检查是否有足够的解
    return rowCount >= cols;
  }

    /**
     * 高斯消元法求解线性方程组
     * @param {number[][]} matrix 增广矩阵
     * @param {number} n 变量数量
     * @param {number} dataSize 数据部分大小
     * @returns {boolean} 是否成功求解
     */
    gaussianElimination(matrix, n, dataSize) {
        for (let col = 0; col < n; col++) {
            // 寻找主元
            let pivotRow = -1;
            for (let row = col; row < matrix.length; row++) {
                if (matrix[row][col] === 1) {
                    pivotRow = row;
                    break;
                }
            }

            if (pivotRow === -1) {
                return false; // 无法找到主元，系统无解
            }

            // 交换行
            if (pivotRow !== col) {
                [matrix[col], matrix[pivotRow]] = [
                    matrix[pivotRow],
                    matrix[col],
                ];
            }

            // 消元
            for (let row = 0; row < matrix.length; row++) {
                if (row !== col && matrix[row][col] === 1) {
                    // 异或操作
                    for (let i = col; i < n + dataSize; i++) {
                        matrix[row][i] ^= matrix[col][i];
                    }
                }
            }
        }

        return true;
    }

    /**
     * 将解码后的块合并为原始数据（移至解码器）
     * @returns {Uint8Array} 原始数据
     */
    getDecodedData() {
        if (!this.isFullyDecoded()) {
            throw new Error("尚未完全解码，无法获取完整数据");
        }

        const result = new Uint8Array(this.originalSize);
        let offset = 0;

        for (const block of this.decodedBlocks) {
            const copySize = Math.min(block.length, this.originalSize - offset);
            result.set(block.subarray(0, copySize), offset);
            offset += copySize;

            if (offset >= this.originalSize) {
                break;
            }
        }

        return result;
    }
}
